package com.example.carmodel;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;

public class IdGenerator {

	private static final Logger log = LoggerFactory.getLogger(IdGenerator.class);

	private static final long twepoch = 12888349746579L;
	// 机器标识位数
	private static final long machineIdBits = 5L;
	// 进程标识位数
	private static final long processIdBits = 5L;
	// 毫秒内自增位数
	private static final long sequenceBits = 12L;
	// 机器ID偏左移12位
	private static final long machineIdShift = sequenceBits;
	// 数据中心ID左移17位
	private static final long processIdShift = sequenceBits + machineIdBits;
	// 时间毫秒左移22位
	private static final long timestampLeftShift = sequenceBits + machineIdBits + processIdBits;
	//sequence掩码，确保sequnce不会超出上限
	private static final long sequenceMask = -1L ^ (-1L << sequenceBits);
	//机器掩码，确保不会超出上限
	private static final long machineMask = -1L ^ (-1L << machineIdBits);
	//进程掩码，确保不会超出上限
	private static final long processMask = -1L ^ (-1L << processIdBits);
	//上次时间戳
	private long lastTimestamp = -1L;
	//序列
	private long sequence = 0L;
	//服务器ID
	private long machineId = 1L;
	//进程编码
	private long processId = 1L;

	public IdGenerator() {
		//获取机器编码
		this.machineId = this.getMachineNum();
		//获取进程编码
		this.processId = this.getProcessNum();
		//避免编码超出最大值
		this.machineId = machineId & machineMask;
		this.processId = processId & processMask;
	}

	public String generator() {
		return this.getNextId()+"";
	}

	private synchronized long getNextId() {
		//获取时间戳
		long timestamp = timeGen();
		//如果时间戳小于上次时间戳则报错
		if (timestamp < lastTimestamp) {
			try {
				throw new Exception("Clock moved backwards.  Refusing to generate id for " + (lastTimestamp - timestamp) + " milliseconds");
			} catch (Exception e) {
				log.error("", e);
			}
		}
		//如果时间戳与上次时间戳相同
		if (lastTimestamp == timestamp) {
			// 当前毫秒内，则+1，与sequenceMask确保sequence不会超出上限
			sequence = (sequence + 1) & sequenceMask;
			if (sequence == 0) {
				// 当前毫秒内计数满了，则等待下一秒
				timestamp = tilNextMillis(lastTimestamp);
			}
		} else {
			sequence = 0;
		}
		lastTimestamp = timestamp;
		// ID偏移组合生成最终的ID，并返回ID
		long nextId = ((timestamp - twepoch) << timestampLeftShift) | (processId << processIdShift) | (machineId << machineIdShift) | sequence;
		return nextId;
	}

	/**
	 * 再次获取时间戳直到获取的时间戳与现有的不同
	 * @param lastTimestamp
	 * @return 下一个时间戳
	 */
	private long tilNextMillis(final long lastTimestamp) {
		long timestamp = this.timeGen();
		while (timestamp <= lastTimestamp) {
			timestamp = this.timeGen();
		}
		return timestamp;
	}

	/**
	 * 获取当前毫秒
	 * @return
	 */
	private long timeGen() {
		return System.currentTimeMillis();
	}

	/**
	 * 获取进程编码
	 * @return
	 */
	private long getProcessNum() {
		RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
		return Long.valueOf(runtimeMXBean.getName().split("@")[0]).longValue();
	}

	/**
	 * 获取机器编码
	 * @return
	 */
	private long getMachineNum(){
		long machinePiece;
		StringBuilder sb = new StringBuilder();
		Enumeration<NetworkInterface> interfaceEnumeration = null;
		try {
			interfaceEnumeration = NetworkInterface.getNetworkInterfaces();
			while (interfaceEnumeration.hasMoreElements()) {
				NetworkInterface networkInterface = interfaceEnumeration.nextElement();
				sb.append(networkInterface.toString());
			}
			machinePiece = sb.toString().hashCode();
			return machinePiece;
		} catch (SocketException e) {
			log.warn("", e);
			return 1L;
		}
	}
}